package com.inspur.edp.bef.bemanager.rtmodule;

import com.inspur.edp.bef.bemanager.dependency.MavenDependency;
import com.inspur.edp.bef.bemanager.dependency.MavenDependencyConst;
import com.inspur.edp.bef.bemanager.dependency.ModifyPomUtil;
import com.inspur.edp.bef.bemanager.util.BefFileUtil;
import com.inspur.edp.bef.bizentity.exceptions.BusinessEntityErrorCodeEnum;
import com.inspur.edp.bef.bizentity.exceptions.BusinessEntityException;
import com.inspur.edp.bef.rtgenerator.lcpfactory.BefCompileManager;
import com.inspur.edp.bff.rtgenerator.fsmanagerfactory.BffCompileManager;
import com.inspur.edp.caf.generator.compileunit.CompileUnitGenerator;
import com.inspur.edp.cef.rtgenerator.common.CefPreCompileManager;
import com.inspur.edp.cef.rtgenerator.common.CompileContext;
import com.inspur.edp.jittojava.context.service.CommonService;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.service.FileService;
import com.inspur.edp.lcm.metadata.api.service.MetadataService;
import com.inspur.edp.lcm.metadata.devcommon.ManagerUtils;
import com.inspur.edp.udt.rtgenerator.common.UdtCompileManager;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.Element;
import com.inspur.edp.caf.generator.module.ModuleGenerator;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Slf4j
public class RuntimeModuleUtil {
	private MetadataService metadataService;
	private CommonService commonService;
	private FileService fileService;
	private String shortRuntimeModuleName = "runtime";
//	private String projectPath = "E:\\projects\\Scm\\SD\\SalesOrder\\bo-salesorder-onlybe";
//	private String sourcePath = projectPath + "\\java\\Scm-SD-SalesOrder\\" +
//			"Scm-SD-SalesOrder-runtime\\src\\main\\java";
//	private String targetPath = projectPath + "\\java\\Scm-SD-SalesOrder\\" +
//			"Scm-SD-SalesOrder-runtime\\target\\classes";
//	private String serverPath = "E:\\gsp\\gsp_cloud\\gs-cloud-ds\\jstack";
//	//core的jar包全名
//	private String coreJarFileName = projectPath + "\\java\\Scm-SD-SalesOrder\\out" +
//			"\\artifacts\\com_inspur_gs_scm_sd_salesorder_core\\com.inspur.gs.scm.sd.salesorder.core.jar";
//	//api的jar包全名
//	private String apiJarFileName = projectPath + "\\java\\Scm-SD-SalesOrder\\out" +
//			"\\artifacts\\com_inspur_gs_scm_sd_salesorder_api\\com.inspur.gs.scm.sd.salesorder.api.jar";
//	private String jarFileName = "com.inspur.gs.scm.sd.salesorder.runtime";
//

	private RuntimeModuleUtil() {
	}

	public static RuntimeModuleUtil getInstance() {
		return new RuntimeModuleUtil();
	}

	/**
	 * 运行时生成
	 *
	 * @param metadataProjPath
	 * @return 新增runtime-module路径
	 */
	public void rtGenerate(String metadataProjPath) {
		String rtModulePath = addRuntimeModule(metadataProjPath);
		// 触发运行时生成
		triggerRuntimeGenerate(metadataProjPath, rtModulePath);
	}

	private String addRuntimeModule(String metadataProjPath) {
		// 待实现
//		String projectPath = getCommonService().getProjectPath(metadataProjPath);
		String relativeJavaProjPath = getJavaProjPath(metadataProjPath);
		String javaProjPath = getAbsolutePath(relativeJavaProjPath);
		String projectPomPath = BefFileUtil.getPomFilePath(javaProjPath);
		Document doc = BefFileUtil.readDocument(projectPomPath);
		boolean isShortNameModule = isShortNameModule(doc.getRootElement());

		MavenDependency projectMavenInfo = readProjectMavenInfo(doc.getRootElement());
		String runtimeModuleArtifactId = getRtModuleArtifactName(projectMavenInfo.getArtifactId());
		String longModulePath = javaProjPath + "\\" + runtimeModuleArtifactId;
		String shortModulePath = javaProjPath + "\\" + shortRuntimeModuleName;
		// 1.已生成精简后runtime模块路径，无处理
		if (isShortNameModule && getFileService().isDirectoryExist(shortModulePath)) {
			return shortModulePath;
		}
		// 2.已生成普通runtime模块路径，若为精简模块名的工程，需重命名路径，修改工程pom文件
		if (getFileService().isDirectoryExist(longModulePath)) {
			if (isShortNameModule) {
				handleShortModulePath(longModulePath, shortModulePath, doc, projectPomPath);
				return shortModulePath;
			}
			return longModulePath;
		}
		// 3.未生成runtime模块
		// 添加runtime模块
		excecuteCommand(javaProjPath, projectMavenInfo, runtimeModuleArtifactId);
		// 安装依赖
		ArrayList<MavenDependency> dependencies = readRuntimeModuleDependency(projectMavenInfo);
		// TODO:jar包位置未确定，暂不安装api,core到本地仓库
		//ManageProjectDependencyUtil.getInstance().installPackage();
		// 添加引用
		ModifyPomUtil.getInstance().modifyPom(longModulePath,
				dependencies);
		// 处理模块名
		if (isShortNameModule) {
			// 此处doc需重新获取
			doc = BefFileUtil.readDocument(projectPomPath);
			handleShortModulePath(longModulePath, shortModulePath, doc, projectPomPath);
			return shortModulePath;
		}
		return longModulePath;
	}

	private void handleShortModulePath(String longModulePath, String shortModulePath, Document doc, String pomPath) {
		// 重命名runtime路径文件夹
		try {
			getFileService().renameDirectory(longModulePath, shortModulePath);
		} catch (IOException e) {
			throw BusinessEntityException.createException(BusinessEntityErrorCodeEnum.GSP_BEMODEL_BUSINESS_ENTITY_0057, e, longModulePath);
		}

		// 修改工程pom中module名
		List<Element> moduleElements = doc.getRootElement().element(MavenDependencyConst.modules).elements();
		for (Element ele : moduleElements) {
			if (ele.getText().contains("runtime")) {
				ele.setText(shortRuntimeModuleName);
				BefFileUtil.saveDocument(doc, new File(pomPath));
				return;
			}
			continue;
		}
		throw BusinessEntityException.createException(BusinessEntityErrorCodeEnum.GSP_BEMODEL_BUSINESS_ENTITY_0058, pomPath);
	}

	private boolean isShortNameModule(Element projectElement) {
		Element modulesElement = projectElement.element(MavenDependencyConst.modules);
		List<Element> moduleElements = modulesElement.elements();
		boolean isShortNameModule = false;
		for (Element ele : moduleElements) {
			if (ele.getText().equals("api")) {
				return true;
			}
			continue;
		}
		return isShortNameModule;
	}

	private void triggerRuntimeGenerate(String metadataProjPath, String rtModulePath) {
		String absoluteProjPath = getAbsolutePath(metadataProjPath);
		String rtSourcePath = rtModulePath + "\\src\\main\\java";
		String rtTargetPath = rtModulePath + "\\target";
		if (!getFileService().isDirectoryExist(rtTargetPath)) {
			getFileService().createDirectory(rtTargetPath);
		}

		if(getFileService().isDirectoryExist(rtSourcePath)){
			try {
				getFileService().deleteAllFilesUnderDirectory(rtSourcePath);
			} catch (Exception e) {
				log.error("Runtime代码清理失败",e.getMessage());
				//throw new RuntimeException("Runtime代码清理失败",e);
				//如果删除失败，重新生成即可。不影响主流程，不需要因为删除失败终止程序
			}
		}

		List<String> projectJarPaths = null;

		projectJarPaths = getCommonService().getJarPath(absoluteProjPath);


		String apiJarFileFullName = getApiJarFullName(absoluteProjPath, projectJarPaths);
		String coreJarFileFullName = getCoreJarFullName(absoluteProjPath, projectJarPaths);
		String compJarFileFullName = getCompJarFullName(absoluteProjPath, projectJarPaths);
		String rtJarFileName = getRtJarFileName(apiJarFileFullName);
		String serverPath = getServerPath();

		String str=getGenTempFolderPath(getJavaProjPath(metadataProjPath));
		String curDateString  =getCurrentDateString();
		tryDeleteOldFiles(str);
		apiJarFileFullName=copyApiJarFile(apiJarFileFullName,str,curDateString);
		coreJarFileFullName=copyCoreJarFile(coreJarFileFullName,str,curDateString);
		if(!"".equals(compJarFileFullName))
			compJarFileFullName = copyCoreJarFile(compJarFileFullName,str,curDateString);
		triggerRuntimeGenerate(
				metadataProjPath,
				rtSourcePath,
				rtTargetPath,
				rtJarFileName,
				serverPath,
				coreJarFileFullName,
				apiJarFileFullName,
				compJarFileFullName);
	}

	private void tryDeleteOldFiles(String path) {
		File dir=new File(path);
		if(dir.exists()==false)
			dir.mkdir();
		if(dir.listFiles()==null)
		    return;
		for (File file:dir.listFiles())
		{
			if(file.isDirectory()==false)
				continue;
			for (File file1:file.listFiles())
			{
				if(file1.isDirectory())
					continue;
				try
				{
					file1.delete();
				}
				catch (Exception e)
				{
					log.error(e.getMessage());
				}
			}
			try
			{
				file.delete();
			}
			catch (Exception e)
			{
				log.error(e.getMessage());
			}
		}
	}

	private String copyApiJarFile(String apiJarFileFullName, String str, String curDateString) {
		File file =new File(apiJarFileFullName);
		Path targetFile = Paths.get(str+"\\"+curDateString+"\\"+file.getName());
		try {
		    File file1=new File(str+"\\"+curDateString+"\\"+file.getName());
		    File dir =new File(str+"\\"+curDateString);
		    if(dir.exists()==false)
		        dir.mkdir();
		    if(file1.exists()==false)
                file1.createNewFile();
			Files.copy(file.toPath(), targetFile,StandardCopyOption.REPLACE_EXISTING);
		} catch (IOException e) {
			throw BusinessEntityException.createException(BusinessEntityErrorCodeEnum.GSP_BEMODEL_BUSINESS_ENTITY_0085, e, file.toString());
		}
		return new File(str+"\\"+curDateString+"\\"+file.getName()).getAbsolutePath();
	}

	private String copyCoreJarFile(String coreJarFileFullName, String str, String curDateString) {
		File file =new File(coreJarFileFullName);
		Path targetFile = Paths.get(str+"\\"+curDateString+"\\"+file.getName());
		try {
            File file1=new File(str+"\\"+curDateString+"\\"+file.getName());
            if(file1.exists()==false)
                file1.createNewFile();
			Files.copy(file.toPath(),targetFile,StandardCopyOption.REPLACE_EXISTING);
		} catch (IOException e) {
			throw BusinessEntityException.createException(BusinessEntityErrorCodeEnum.GSP_BEMODEL_BUSINESS_ENTITY_0085, e, file.toString());
		}
		return new File(str+"\\"+curDateString+"\\"+file.getName()).getAbsolutePath();
	}


	private String getGenTempFolderPath(String javaProjPath) {
		String outPath = getAbsolutePath(javaProjPath)+"\\out";
		File outFile= new File(outPath);
		if(outFile.exists()==false)
			outFile.mkdir();

		String tempPath = outPath +"\\genTemp";
		File file= new File(tempPath);
		if(file.exists()==false)
			file.mkdir();
		return tempPath;
	}

	private String getCurrentDateString()
	{
		Date date =new Date();
		StringBuilder stringBuilder=new StringBuilder();
		stringBuilder.append(date.getYear()).append(date.getMonth()).append(date.getDay()).append(date.getHours()).append(date.getMinutes()).append(date.getSeconds());
		return stringBuilder.toString();
	}

	public void triggerRuntimeGenerate(String metadataProjPath, String rtSoucePath, String rtTargetPath,
	                                   String jarFileName,
	                                   String serverPath, String coreJarFileName, String apiJarFileName,String compJarFileName) {
//		System.out.println("开始生成runtime代码");
		CompileContext context = createCompileContext(metadataProjPath, coreJarFileName, apiJarFileName, rtSoucePath,
				serverPath, compJarFileName);
		List<GspMetadata> metadataList = getProjGspMetadataList(metadataProjPath);
		for (GspMetadata meta : metadataList) {
			triggerMetadataRtGenerator(meta, context);
		}
		context.getBaseGenerator().initialize();
		context.getBaseGenerator().generate();

		ArrayList<String> jarPaths = new ArrayList<String>();
		jarPaths.add(getAbsolutePath(metadataProjPath));
		jarPaths.add(serverPath);
//		System.out.println("runtime代码生成完成");
//		context.getBaseGenerator().build(rtSoucePath, rtTargetPath, jarPaths, jarFileName,
//				rtTargetPath);
	}

	//region 添加模块
	private String getRtModuleArtifactName(String projectArtifactName) {
		return projectArtifactName + "-runtime";
	}

	private ArrayList<MavenDependency> readRuntimeModuleDependency(MavenDependency projectMavenInfo) {
		ArrayList<MavenDependency> dependencies = new ArrayList<>();
		String groupId = projectMavenInfo.getGroupId();
		String version = projectMavenInfo.getVersion();
		String apiArtifactId = projectMavenInfo.getArtifactId() + "-api";
		String coreArtifactId = projectMavenInfo.getArtifactId() + "-core";
		dependencies.add(new MavenDependency(groupId, apiArtifactId, version));
		dependencies.add(new MavenDependency(groupId, coreArtifactId, version));
		return dependencies;
	}

	private void excecuteCommand(String projectPath, MavenDependency info, String runtimeModuleArtifactId) {
		String command = String.format("cmd /c cd %s", projectPath);
		String groupId = info.getGroupId();
		String addModuleCommand = String.format("mvn archetype:generate -DgroupId=%1$s " +
						" -DartifactId=%2$s -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false",
				groupId, runtimeModuleArtifactId);
		command = command.concat(" && " + addModuleCommand);
		Process process = null;
		BefFileUtil.excecuteCommand(process, command, "添加bef运行时模块");
	}

	private MavenDependency readProjectMavenInfo(Element root) {
		String groupId = BefFileUtil.getTagValue(root, MavenDependencyConst.groupId);
		String artifactId = BefFileUtil.getTagValue(root, MavenDependencyConst.artifactId);
		String version = BefFileUtil.getTagValue(root, MavenDependencyConst.version);
		return new MavenDependency(groupId, artifactId, version);
	}
	//endregion

	// region 路径拼写

	private String getJavaProjPath(String projPath) {
		return BefFileUtil.getJavaProjPath(projPath);
	}

	private String getServerPath() {
		return BefFileUtil.getServerPath();
	}

	private String getApiJarFullName(String projPath, List<String> projectJarPaths) {
		String apiJarFileFullName = "";
		for (String path : projectJarPaths) {
			if(path.contains("out\\genTemp")|| path.contains("out/genTemp"))
				continue;
			if (path.endsWith("-api-0.1.0-SNAPSHOT.jar") || path.endsWith("api.jar") || path.contains("-api-")) {
				apiJarFileFullName = projPath + "\\" + path;
				break;
			}
		}
		if (apiJarFileFullName.isEmpty()) {
			throw BusinessEntityException.createException(BusinessEntityErrorCodeEnum.GSP_BEMODEL_BUSINESS_ENTITY_0060, "api");
		}
		return apiJarFileFullName;
	}

	private String getCompJarFullName(String projPath, List<String> projectJarPaths) {
		String compJarFileFullName = "";
		for (String path : projectJarPaths) {
			if(path.contains("out\\genTemp")|| path.contains("out/genTemp"))
				continue;
			if (path.endsWith("-comp-0.1.0-SNAPSHOT.jar") || path.endsWith("comp.jar") || path.contains("-comp-")) {
				compJarFileFullName = projPath + "\\" + path;
				break;
			}
		}
//		if (compJarFileFullName.isEmpty()) {
//			throw new RuntimeException("无法找到comp的jar包路径");
//		}
		return compJarFileFullName;
	}

	private String getCoreJarFullName(String projPath, List<String> projectJarPaths) {
		String coreJarFileFullName = "";
		for (String path : projectJarPaths) {
			if(path.contains("out\\genTemp") || path.contains("out/genTemp"))
				continue;
			if (path.endsWith("-core-0.1.0-SNAPSHOT.jar") || path.endsWith("core.jar") || path.contains("-core-")) {
				coreJarFileFullName = projPath + "\\" + path;
				break;
			}
		}
		if (coreJarFileFullName.isEmpty()) {
			throw BusinessEntityException.createException(BusinessEntityErrorCodeEnum.GSP_BEMODEL_BUSINESS_ENTITY_0060, "core");
		}
		return coreJarFileFullName;
	}

	private String getRtJarFileName(String apiJarFileFullName) {
		String[] apiJarFileNameList = apiJarFileFullName.split("\\\\");
		String apiJarFileName = apiJarFileNameList[apiJarFileNameList.length - 1];
		String rtJarFileName = apiJarFileName.replace(".api", ".runtime");
		rtJarFileName = rtJarFileName.replace("-api", "-runtime");
		return rtJarFileName;
	}

	private List<String> getMockJarPath() {
		ArrayList<String> list = new ArrayList<>();
		list.add("java\\Scm-SD-SalesOrder\\Scm-SD-SalesOrder-api\\target\\Scm-SD-SalesOrder-api-0.1.0-SNAPSHOT.jar");
		list.add("java\\Scm-SD-SalesOrder\\Scm-SD-SalesOrder-core\\target\\Scm-SD-SalesOrder-core-0.1.0-SNAPSHOT.jar");
		return list;
	}
	// endregion

	//region 运行时生成


	private CompileContext createCompileContext(String metadataProjPath, String coreJarFileName,
	                                            String apiJarFileName, String rtSourcePath, String serverPath,
												String compJarFilelName) {
		CompileContext context = new CompileContext();
		context.setProjPath(metadataProjPath);
		context.setBaseGenerator(createRuntimeModuleGenerator());
		context.setCoreJarFileName(coreJarFileName);
		context.setApiJarFileName(apiJarFileName);
		context.setGeneratedPath(rtSourcePath);
		context.setServerPath(serverPath);
		context.setCompJarFileName(compJarFilelName);
		return context;
	}

	private ModuleGenerator createRuntimeModuleGenerator() {
		return new ModuleGenerator() {
			@Override
			protected ArrayList<CompileUnitGenerator> getCompileUnitGenrators() {
				return null;
			}
		};
	}

	private List<GspMetadata> getProjGspMetadataList(String metaProjPath) {
		return getMetadataService().getMetadataList(metaProjPath);
	}

	private CommonService getCommonService() {
		if (commonService == null) {
			commonService = BefFileUtil.getService(CommonService.class);
		}
		return commonService;
	}

	private MetadataService getMetadataService() {
		if (metadataService == null) {
			metadataService = BefFileUtil.getService(MetadataService.class);
		}
		return metadataService;
	}

	private FileService getFileService() {
		if (fileService == null) {
			fileService = BefFileUtil.getService(FileService.class);
		}
		return fileService;
	}

	private void triggerMetadataRtGenerator(GspMetadata metadata, CompileContext context) {
		CefPreCompileManager compileManager = getRtCompileManager(metadata);
		if (compileManager == null) {
			return;
		}
		compileManager.build(context);
	}

	private CefPreCompileManager getRtCompileManager(GspMetadata metadata) {
		switch (metadata.getHeader().getType()) {
			case "GSPBusinessEntity":
				return new BefCompileManager(loadMetadata(metadata));
			case "GSPViewModel":
				return new BffCompileManager(loadMetadata(metadata));
			case "UnifiedDataType":
				return new UdtCompileManager(loadMetadata(metadata));
			default:
//				throw new RuntimeException("不支持的元数据类型" + type);
				return null;
		}
	}

	private GspMetadata loadMetadata(GspMetadata item) {
		//todo:元数据按relativePath加载存在问题
		return getMetadataService().loadMetadata(item.getHeader().getFileName(),
				item.getRelativePath());
	}

	private String getAbsolutePath(String relativePath) {
//		String devPath = getFileService().getDevRootPath();、
		String devPath = ManagerUtils.getDevRootPath();
		return Paths.get(devPath).resolve(relativePath).toString();
	}
	//endregion
}
